package robot
import (
    "fmt"
    //"os"
    //"io"
    //"bufio"
    "encoding/json"
	"sync"
    io "io/ioutil"
	"github.com/looplab/fsm"
)
/*Robot
*RobotID key of robot ID
*RobotDescFile  description file of robot
*/
var once sync.Once
var pRobotMgr *RobotMgr
type Robot struct {
	Id          string
	DescFile    string
	Desc        RobotDesc
	//Fsm         *fsm.FSM
	Events      [] fsm.EventDesc
	State2Index map[string] int
}
type RobotMgr struct {
	Id2RobotMap  map[string] *Robot
}
type RobotStateTrans struct{
	Event  string `json:"event"`
	Cond   string `json:"cond"`
	Target string `json:"target"`
}
type RobotOutput struct{
	Type    string `json:"type"`
	Content string `json:"content"`
}

type RobotFlow struct{
	State         string `json:"state"`
	Output        RobotOutput `json:"output"`
	Transition    [] RobotStateTrans `json:"transition"`    
	Action        string  `json:"action"`
}
type RobotDesc struct{
	RobotId        string `json:"botId"`
	Initialstate   string `json:"initialstate"`
	Flow    []RobotFlow   `json:"flow"`
}

func GetRobotMgr() *RobotMgr{
	once.Do( func() {
		pRobotMgr = CreateRobotMgr()
	})
	return pRobotMgr
}
func CreateRobotMgr() *RobotMgr{
	newRobotMgr := new(RobotMgr)
	newRobotMgr.Id2RobotMap = make(map[string] *Robot)
	return newRobotMgr
}
func PulishRobot(mgr *RobotMgr,robotId string, robotFile  string) (*Robot,error){
	pRobot, ok := mgr.Id2RobotMap[robotId]
	if (ok){
		//delete old robot
		delete(mgr.Id2RobotMap,robotId)
	}
	pRobot	= CreateRobot(mgr,robotId,robotFile);
	err := LoadRobot(pRobot,pRobot.DescFile)			
	return pRobot,err
}
func GetRobot(mgr *RobotMgr,robotId string, robotFile  string) (*Robot,error){
	//if not exist, just create it and load it,otherwise just return its pointer
	pRobot, ok := mgr.Id2RobotMap[robotId]
	fmt.Println(ok)
	if (!ok){
		fmt.Printf("no such robot\n")
		pRobot	= CreateRobot(mgr,robotId,robotFile);
		err := LoadRobot(pRobot,pRobot.DescFile)			
		if( nil != err){
			return nil,err
		}
	}
	return pRobot,nil
}
func CreateRobot(mgr *RobotMgr,robotId string, robotFile  string) *Robot{
	newRobot := new (Robot)
	newRobot.Id = robotId
	newRobot.DescFile = robotFile
	newRobot.State2Index = make(map[string] int)
	mgr.Id2RobotMap[robotId] = newRobot
	
	return newRobot
}


/*LoadRobot
Load robot desciption file
*/
func LoadRobot(robot *Robot, fileName string) error {
	data, _ := io.ReadFile(fileName)
	datajson := []byte(data)
	if err := json.Unmarshal(datajson, &robot.Desc); err != nil {
		fmt.Println("转换失败")
		return err
	}else{
		fmt.Println("collectStates")
		collectStates(robot)
	}
	
	//fmt.Println(robot.Desc)
	//fmt.Println(robot.Desc.RobotId)
	//fmt.Println(cap(robot.Desc.Flow))
	return nil
}
func collectStates(pRobot *Robot){
	var events []fsm.EventDesc
	robotDesc := pRobot.Desc
	for k,v := range robotDesc.Flow {
		curState := v.State 
		pRobot.State2Index[curState]=k
		for _,v1 := range robotDesc.Flow[k].Transition {
			target := v1.Target
			event  := v1.Event
			events = append(events,fsm.EventDesc{Name:event,Src:[]string{curState},Dst:target})
		}
	}
	pRobot.Events = events
	//pRobot.Fsm = fsm.NewFSM("idle",events,fsm.Callbacks{},)
	//fmt.Printf("Fsm get pointer %p", pRobot.Fsm)
}
func GetStateIndex( pRobot *Robot, state string) int{
	return pRobot.State2Index[state]
}
func MatchCond( input string,pRobot *Robot, index int) string{
	trans := pRobot.Desc.Flow[index].Transition
	var cond string
	var event string
	for _,v1 := range trans{
		cond = v1.Cond
		event = v1.Event
		if (input == cond){
			fmt.Printf("Match cond %s, event %s\n",cond, event)
			return event
		}else{
			fmt.Printf("do not Match cond %s, event %s, input %s\n",cond, event, input)
		}
		
	}
	return ""
}
func GetResponse(pRobot *Robot, index int) string{
	return pRobot.Desc.Flow[index].Output.Content
}
